#lang racket
;; MIT License

;; Copyright (c) 2025 三体自动化技术协会

;; Permission is hereby granted, free of charge, to any person obtaining a copy
;; of this software and associated documentation files (the "Software"), to deal
;; in the Software without restriction, including without limitation the rights
;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
;; copies of the Software, and to permit persons to whom the Software is
;; furnished to do so, subject to the following conditions:

;; The above copyright notice and this permission notice shall be included in all
;; copies or substantial portions of the Software.

;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
;; SOFTWARE.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; JUST type "racket server.rkt" to run the server in the project directory ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require web-server/servlet
         web-server/servlet-env
		 web-server/http/request-structs
         json)

; 保存项目目录
(define project-dir (current-directory))

; 在加载Tao5.rkt之前切换到项目目录
(current-directory project-dir)
(require "Tao5.rkt")

; 全局状态锁
(define machine-mutex (make-semaphore 1))

; 读取index.html文件内容的函数
(define (read-index-html)
  (if (file-exists? "index.html")
      (with-input-from-file "index.html"
        (lambda ()
          (port->string)))
      "<h1>Index.html not found</h1>"))

; 读取frame-buffer.html文件内容的函数
(define (read-frame-buffer-html)
  (if (file-exists? "frame-buffer.html")
      (with-input-from-file "frame-buffer.html"
        (lambda ()
          (port->string)))
      "<p>Frame buffer not available</p>"))

; 获取帧缓冲区数据的函数


; 静态文件服务函数
(define (serve-static-file path content-type)
  (lambda (req)
    (if (file-exists? path)
        (response/full
         200 #"OK"
         (current-seconds) (string->bytes/utf-8 content-type)
         (list)
         (list (file->bytes path)))
        (response/full
         404 #"Not Found"
         (current-seconds) #"text/plain"
         (list)
         (list (string->bytes/utf-8 "File not found"))))))

; 路由
(define-values (dispatch req)
  (dispatch-rules
   [("")  ; 根路径
    (lambda (req)
      (response/full
       200 #"OK"
       (current-seconds) #"text/html; charset=utf-8"
       (list)
       (list (string->bytes/utf-8 (read-index-html)))))]

   [("styles.css")  ; CSS文件
    (serve-static-file "styles.css" "text/css; charset=utf-8")]

   [("script.js")   ; JavaScript文件
    (serve-static-file "script.js" "application/javascript; charset=utf-8")]

   [("frame-buffer.html")  ; 单独访问frame-buffer.html
    (lambda (req)
      (response/full
       200 #"OK"
       (current-seconds) #"text/html; charset=utf-8"
       (list)
       (list (string->bytes/utf-8 (read-frame-buffer-html)))))]

   [("api" "state") #:method "get" ;; 获取机器当前状态
					(lambda (req)
					  (call-with-semaphore
					   machine-mutex
					   (λ ()
						 (response/jsexpr
						  (hash 'coins COINS-REMAIN
								'pc PC
								'sp SP
								'cycles CYCLES
								'coins COINS-REMAIN
								'stack (vector->list REGSTACK)
								'mem (vector->list MEM))))))]

   [("api" "frame-buffer") #:method "get" ;; 获取帧缓冲区数据
						   (lambda (req)
							 (call-with-semaphore
							  machine-mutex
							  (λ ()
								(if (file-exists? "frame-buffer.html")
									(response/jsexpr
									 (hash 'success #t
										   'frameBuffer (file->string "frame-buffer.html")))
									(response/jsexpr
									 (hash 'success #f
										   'error "Frame buffer file not found"))))))]

   [("api" "run") #:method "post"
				  (lambda (req)
					(call-with-semaphore
					 machine-mutex
					 (λ ()
					   (define result (RUN-MACHINE!))
					   (render-frame-buffer)
					   (response/jsexpr
						(hash 'result result
							  'pc PC
							  'cycles CYCLES
							  'coins COINS-REMAIN)))))]

   [("api" "step") #:method "post"
				   (lambda (req)
					 (call-with-semaphore
					  machine-mutex
					  (λ ()
						(step-machine!)
						(dump-machine)
						(render-frame-buffer)
						(response/jsexpr
						 (hash 'success #t
							   'pc PC
							   'cycles CYCLES
							   'coins COINS-REMAIN)))))]

   [("api" "reset") #:method "put"
					(lambda (req)
					  (call-with-semaphore
					   machine-mutex
					   (λ ()
						 (RESET-MACHINE!)
						 (response/jsexpr (hash 'success #t
												'pc PC
												'cycles CYCLES
												'coins COINS-REMAIN)))))]

   [("api" "insert-coin") #:method "post"
						  (lambda (req)
							(call-with-semaphore
							 machine-mutex
							 (λ ()
							   (insert-coin 100)
							   (render-frame-buffer)
							   (response/jsexpr (hash 'success #t
													  'pc PC
													  'cycles CYCLES
													  'coins COINS-REMAIN)))))]
   [("api" "asm") #:method "post"
				  (lambda (req)
					(call-with-semaphore
					 machine-mutex
					 (λ ()
					   (define post-data (bytes->jsexpr (request-post-data/raw req)))
					   (define asm-str (hash-ref post-data 'code #f))

					   (if asm-str
						   (with-handlers ([exn? (lambda (e)
												   (response/jsexpr
													(hash 'success #f
														  'error (exn-message e))))])
							 ;; 核心三行代码：自动添加括号 -> 读取 -> 汇编 -> 加载
							 (define wrapped-str (string-append "(" asm-str ")"))
							 (define code-list (read (open-input-string wrapped-str)))
							 (define bytecode (ASSEMBLE code-list))
							 (RESET-MACHINE!)
							 (LOAD-BYTECODE! bytecode)

							 (response/jsexpr
							  (hash 'success #t
									'bytecodeLength (vector-length bytecode)
									'pc PC
									'coins COINS-REMAIN)))
						   (response/jsexpr
							(hash 'success #f
								  'error "Missing 'code' field in request"))))))]
   ))

; 初始化
(RESET-MACHINE!)

; 启动服务器
(serve/servlet dispatch
               #:port 8080
               #:servlet-regexp #rx""  ; 处理所有路径
               #:listen-ip #f
               #:command-line? #t
               #:servlet-current-directory project-dir)
